import { getWorkerPool, workerChunk } from "@/worker"; // 确保路径正确
import { startUploadSession, completeFileUpload, finishUploadSession } from "./processFile.api";
import type { CompleteFileUploadParams } from "./processFile.api";

const CHUNK_SIZE = 1024 * 1024 * 5; // 每个 chunk 5MB
// const CHUNK_SIZE = 1024 * 1024 * 5; // 每个 chunk 5MB
const HASH_ALGORITHM = "SHA-256"; // 哈希算法
const MAX_CONCURRENT_REQUESTS = 5; // 同时最大请求数量

type ChunkRespond = {
    success?: boolean;
    sessionID: string;
    hashHex: string;
    index: number;
};

const processChunkWorker = async (blob: Blob, sessionID: string, index: number) => {
    return await workerChunk(
        async (
            depts,
            blob: Blob,
            algorithm: string = "SHA-256",
            sessionID: string,
            index: number
        ) => {
            const hashHex = await depts.calculateChunkHash(blob, algorithm);
            const response = await depts.attemptChunkPost(
                blob,
                sessionID,
                algorithm,
                hashHex,
                index
            );
            return response;
        },
        blob,
        HASH_ALGORITHM,
        sessionID,
        index
    );
};

async function processChunk(
    file: File,
    chunkSize: number,
    sessionID: string,
    index: number
): Promise<ChunkRespond> {
    const start = index * chunkSize;
    const end = Math.min(start + chunkSize, file.size);
    const blob = file.slice(start, end);
    const response = await processChunkWorker(blob, sessionID, index);
    console.log(`Chunk ${index} processed`);
    return response;
}

// 结束会话
const endSession = async (sessionID: string) => {
    try {
        const response = await finishUploadSession(sessionID);
        if (!response.sessionID) throw new Error("Failed to finish session");
    } catch (error) {
        // TODO 处理结束会话错误
        console.error(error);
    }
};

const getFileData = (file: File) => {
    // TODO 处理文件名 在完成上传之前检验文件名是否合法
    const suffix = "." + file.name.split(".").pop();
    const name = file.name.replace(`${suffix}`, "");
    if (!suffix) throw new Error("Invalid file suffix");
    if (!name) throw new Error("Invalid file name");
    return { suffix, name };
};

// 完成上传
const completeUpload = async (file: File, sessionID: string) => {
    try {
        const params: CompleteFileUploadParams = {
            sessionID,
            ...getFileData(file)
        };
        const success = await completeFileUpload(params);
        if (!success) throw new Error("Failed to finish session");
    } catch (error) {
        // TODO 处理结束会话错误
        console.error(error);
    }
};

const uploadFile = async (file: File, sessionID: string): Promise<void> => {
    const chunkCount = Math.ceil(file.size / CHUNK_SIZE);
    let current = 0; // 当前正在处理的 chunk 下标喵

    const processChunksConcurrently = async (): Promise<void> => {
        while (current < chunkCount) {
            const promises: Promise<ChunkRespond>[] = [];

            for (let i = 0; i < MAX_CONCURRENT_REQUESTS && current < chunkCount; i++) {
                const chunkIndex = current++;
                const promise = processChunk(file, CHUNK_SIZE, sessionID, chunkIndex);
                promises.push(promise);
            }

            await Promise.all(promises);
        }
    };

    await processChunksConcurrently();

    console.log("所有 chunk 上传完成喵~");
};

// 开启会话
const initiateSession = async () => {
    try {
        const response = await startUploadSession();
        if (!response.success) throw new Error("Session creation failure");
        return response.sessionID;
    } catch (error) {
        console.error(error);
        throw new Error("Failed to start session");
    }
};

// 处理文件
const processFile = async (file: File) => {
    try {
        const sessionID = await initiateSession();
        await uploadFile(file, sessionID);
        await completeUpload(file, sessionID);
        await endSession(sessionID);
    } catch (error) {
        // TODO 处理上传错误
        console.error(error);
    }
};

export default processFile;
